// Package csr 提供本地私钥与 CSR 生成
package csr

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/sha256"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/hex"
    "encoding/pem"
)

type KeyOptions struct { Type string; Size int; Curve string }
type CSROptions struct {
    CommonName string
    Organization string
    Country string
    State string
    Locality string
    Email string
}

func GenerateKeyAndCSR(keyOpt KeyOptions, csrOpt CSROptions) (string, string, string, error) {
    if keyOpt.Type == "" { keyOpt.Type = "rsa" }
    if keyOpt.Type == "rsa" && keyOpt.Size == 0 { keyOpt.Size = 2048 }
    if keyOpt.Type == "ecdsa" && keyOpt.Curve == "" { keyOpt.Curve = "prime256v1" }
    var priv interface{}
    var err error
    switch keyOpt.Type {
    case "rsa":
        priv, err = rsa.GenerateKey(rand.Reader, keyOpt.Size)
    case "ecdsa":
        var curve elliptic.Curve
        switch keyOpt.Curve {
        case "prime256v1": curve = elliptic.P256()
        case "secp384r1": curve = elliptic.P384()
        case "secp521r1": curve = elliptic.P521()
        default: curve = elliptic.P256()
        }
        priv, err = ecdsa.GenerateKey(curve, rand.Reader)
    default:
        priv, err = rsa.GenerateKey(rand.Reader, 2048)
    }
    if err != nil { return "", "", "", err }
    subj := pkix.Name{CommonName: csrOpt.CommonName}
    if csrOpt.Organization != "" { subj.Organization = []string{csrOpt.Organization} }
    if csrOpt.Country != "" { subj.Country = []string{csrOpt.Country} }
    if csrOpt.State != "" { subj.Province = []string{csrOpt.State} }
    if csrOpt.Locality != "" { subj.Locality = []string{csrOpt.Locality} }
    tpl := x509.CertificateRequest{Subject: subj}
    der, err := x509.CreateCertificateRequest(rand.Reader, &tpl, priv)
    if err != nil { return "", "", "", err }
    csrPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: der})
    var keyPEM []byte
    switch k := priv.(type) {
    case *rsa.PrivateKey:
        keyPEM = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)})
    case *ecdsa.PrivateKey:
        b, e := x509.MarshalECPrivateKey(k); if e != nil { return "", "", "", e }
        keyPEM = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
    }
    sum := sha256.Sum256(csrPEM)
    return string(keyPEM), string(csrPEM), hex.EncodeToString(sum[:]), nil
}
